/*
 This file is part of Desu: MapleStory v62 Server Emulator
 Copyright (C) 2014  Brent Rinchiuso <brentrin@gmail.com>

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package field;

import data.WzDataProvider;
import java.util.LinkedHashMap;
import java.util.concurrent.locks.ReentrantLock;
import service.Configuration;
import util.StringUtil;
import wz.WzImage;
import wz.WzObject;
import wz.common.WzDataTool;

/**
 *
 * @author Brent Rinchiuso
 */
public final class FieldManager {

    private static final ReentrantLock fieldLock = new ReentrantLock(true);
    private static final ReentrantLock instanceLock = new ReentrantLock(true);
    private static final LinkedHashMap<Integer, Field> fields = new LinkedHashMap<>();
    private static final LinkedHashMap<String, Field> instancedFields = new LinkedHashMap<>();

    private FieldManager() {
    }

    public static Field getField(int id) {
        return getField(id, false, true, true);
    }

    public static Field getField(int id, boolean reload) {
        return getField(id, reload, true, true);
    }

    public static Field getField(int id, boolean reload, boolean life, boolean reactors) {
        fieldLock.lock();
        try {
            if (reload) {
                Field f = fields.remove(id);
                if (f != null) {
                    f.purge();
                }
                // XXX may want to force respawn for players
                // in the old field if this is to occur, however
                // I doubt that it is going to happen where we require a reload
            } else if (fields.containsKey(id)) {
                return fields.get(id);
            }
            Field ret = createField(id, life, reactors);
            if (ret != null) {
                fields.put(id, ret);
            }
            return ret;
        } finally {
            fieldLock.unlock();
        }
    }

    public static Field createInstancedField(int id, String ik) {
        return createInstancedField(id, ik, true, true);
    }

    public static Field createInstancedField(int id, String ik, boolean life, boolean reactors) {
        instanceLock.lock();
        try {
            if (instancedFields.containsKey(ik)) {
                // may want to implement something different
                // especially if for some reason we can't know
                // what instance key we want to use.
                System.out.println("Tried creating a new instanced field while another one with the same key exists.");
                return null;
            }
            Field ret = createField(id, life, reactors);
            if (ret != null) {
                instancedFields.put(ik, ret);
            }
            return ret;
        } finally {
            instanceLock.unlock();
        }
    }

    public static Field getInstancedField(String ik) {
        return instancedFields.get(ik);
    }

    public static void removeInstancedField(String ik) {
        instancedFields.remove(ik);
    }

    // generic generation code
    private static Field createField(int id, boolean life, boolean reactors) {
        Field ret = new Field(id);
        StringBuilder path = new StringBuilder();
        path.append("Map\\Map");
        path.append(id / 100000000);
        path.append("\\");
        path.append(StringUtil.getLeftPaddedString(String.valueOf(id), '0', 9));
        path.append(".img");
        WzImage data = WzDataProvider.loadImage(Configuration.WZ_DIRECTORY, "Map", path.toString());
        if (data == null) {
            System.out.printf("Tried loading invalid field with path: %s%n", path.toString());
            return null;
        }
        String link = WzDataTool.getString(data, "info/link", null);
        if (link != null) {
            data.unparse();
            Field lField = createField(Integer.parseInt(link), life, reactors);
            lField.forceFieldId(id);
            return lField;
        }
        ret.setMobRate(WzDataTool.getFloat(data, "info/mobRate", 1.0F));
        ret.setReturnField(WzDataTool.getInteger(data, "info/returnMap", 0));
        ret.setFlyEnabled(WzDataTool.getBoolean(data, "info/fly", false));
        ret.setTown(WzDataTool.getBoolean(data, "info/town", false));
        ret.setSwimEnabled(WzDataTool.getBoolean(data, "info/swim", false));
        ret.setOnFirstUserEnter(WzDataTool.getString(data, "info/onFirstUserEnter", null));
        ret.setOnUserEnter(WzDataTool.getString(data, "info/onUserEnter", null));
        ret.setTimeLimit(WzDataTool.getInteger(data, "info/timeLimit", -1));
        ret.setFieldType(WzDataTool.getInteger(data, "info/fieldType", -1));
        ret.setForcedReturn(WzDataTool.getInteger(data, "info/forcedReturn", -1));
        // XXX load footholds required for drop placements etc.
        if (life) {
            for (WzObject o : data.getChild("life")) {
                String t = WzDataTool.getString(o, "type", "");
                switch (t) {
                    case "n":
                        NPC n = new NPC(o);
                        ret.addFieldObject(n);
                        break;
                    case "m":
                        Spawner sp = new Spawner(o);
                        ret.addSpawner(sp);
                        break;
                    default:
                        System.out.printf("New life type discovered: %s%n", t);
                        break;
                }
            }
        }
        ret.parsePortalList(data.getChild("portal"));
        if (reactors) {
            WzObject<?, ?> re = data.getChild("reactor");
            if (re != null) {
                for (WzObject o : re) {
                    ret.addReactor(new Reactor(o));
                }
            }
        }
        ((WzImage) data).unparse();
        return ret;
    }
}
